using Ln.Fram.SqlSugarCore.Abstractions;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;

namespace Ln.Fram.Ddd.Application;

public abstract class AbstractCrudService<TEntity, TEntityDto, TKey, TCreateUpdateInput>(
	ISqlSugarRepository<TEntity> repository)
	: AbstractCrudService<TEntity, TEntityDto, TKey, PagedAndSortedResultRequestDto, TCreateUpdateInput>(repository)
	where TEntity : class, IEntity, new()
{
}

public abstract class AbstractCrudService<TEntity, TEntityDto, TKey, TGetListInput, TCreateUpdateInput>(
	ISqlSugarRepository<TEntity> repository)
	: AbstractCrudService<TEntity, TEntityDto, TKey, TGetListInput, TCreateUpdateInput, TCreateUpdateInput>(repository)
	where TEntity : class, IEntity, new()
{
}

public abstract class AbstractCrudService<TEntity, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>(
	ISqlSugarRepository<TEntity> repository)
	: AbstractCrudService<TEntity, TEntityDto, TEntityDto, TKey, TGetListInput, TCreateInput, TUpdateInput>(repository)
	where TEntity : class, IEntity, new()
{
}

public abstract class AbstractCrudService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput,
	TUpdateInput>(ISqlSugarRepository<TEntity> repository)
	: AbstractReadService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput>(repository)
	where TEntity : class, IEntity, new()
{
	#region 成员属性
	protected virtual string? CreatePolicyName { get; } = null;
	protected virtual string? UpdatePolicyName { get; } = null;
	protected virtual string? DeletePolicyName { get; } = null;
	#endregion

	#region 新增方法
	/// <summary>
	/// 新增单条数据
	/// </summary>
	/// <param name="input">新增数据信息</param>
	/// <returns>新增结果数据</returns>
	public async Task<TGetOutputDto> CreateAsync(TCreateInput input)
	{
		await CreateBeforeAsync(input);
		var entity = await MapToEntityAsync(input);
		TryToSetTenantId(entity);
		await CreateDoneAsync(entity, input);
		return await CreateAfterAsync(entity, input);
	}

	/// <summary>
	/// 新增前的数据检查
	/// </summary>
	/// <param name="input">新增数据信息</param>
	protected virtual async Task CreateBeforeAsync(TCreateInput input)
	{
		await CheckPolicyAsync(CreatePolicyName);
	}

	/// <summary>
	/// 新增的数据库操作
	/// </summary>
	/// <param name="entity">新增数据实体</param>
	/// <param name="input">新增数据信息</param>
	protected virtual async Task CreateDoneAsync(TEntity entity, TCreateInput input)
	{
		await Repository.InsertAsync(entity);
	}

	/// <summary>
	/// 新增后的返回实体
	/// </summary>
	/// <param name="entity">新增数据实体</param>
	/// <param name="input">新增数据信息</param>
	/// <returns>新增数据实体</returns>
	protected virtual async Task<TGetOutputDto> CreateAfterAsync(TEntity entity, TCreateInput input)
	{
		return await MapToGetOutputDtoAsync(entity);
	}
	#endregion

	#region 更新方法
	/// <summary>
	/// 更新单条数据
	/// </summary>
	/// <param name="id">更新数据主键</param>
	/// <param name="input">更新数据信息</param>
	/// <returns>更新结果数据</returns>
	public async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
	{
		var entity = await UpdateBeforeAsync(id, input);
		await MapToEntityAsync(input, entity);
		await UpdateDoneAsync(entity, id, input);
		return await UpdateAfterAsync(entity, id, input);
	}

	/// <summary>
	/// 更新前的数据检查
	/// </summary>
	/// <param name="id">更新数据主键</param>
	/// <param name="input">更新数据信息</param>
	protected virtual async Task<TEntity> UpdateBeforeAsync(TKey id, TUpdateInput input)
	{
		await CheckPolicyAsync(UpdatePolicyName);
		return await GetEntityByIdAsync(id);
	}

	/// <summary>
	/// 更新的数据库操作
	/// </summary>
	/// <param name="entity">更新数据实体</param>
	/// <param name="id">更新数据主键</param>
	/// <param name="input">更新数据信息</param>
	protected virtual async Task UpdateDoneAsync(TEntity entity, TKey id, TUpdateInput input)
	{
		await Repository.UpdateAsync(entity);
	}

	/// <summary>
	/// 更新后的返回实体
	/// </summary>
	/// <param name="entity">更新数据实体</param>
	/// <param name="id">更新数据主键</param>
	/// <param name="input">更新数据信息</param>
	/// <returns>更新数据实体</returns>
	protected virtual async Task<TGetOutputDto> UpdateAfterAsync(TEntity entity, TKey id, TUpdateInput input)
	{
		return await MapToGetOutputDtoAsync(entity);
	}
	#endregion

	#region 删除方法
	/// <summary>
	/// 删除单条数据
	/// </summary>
	/// <param name="id">删除数据主键</param>
	public async Task DeleteAsync(TKey id)
	{
		await DeleteBeforeAsync(id);
		await DeleteDoneAsync(id);
	}

	/// <summary>
	/// 删除前的数据检查
	/// </summary>
	/// <param name="id">删除数据主键</param>
	protected virtual async Task DeleteBeforeAsync(TKey id)
	{
		await CheckPolicyAsync(DeletePolicyName);
	}

	/// <summary>
	/// 删除的数据库操作
	/// </summary>
	/// <param name="id">删除数据主键</param>
	protected abstract Task DeleteDoneAsync(TKey id);
	#endregion

	#region 重写方法
	/// <summary>
	/// 新增对象映射为实体
	/// </summary>
	/// <param name="createInput">新增数据信息</param>
	/// <returns>新增数据实体</returns>
	protected virtual Task<TEntity> MapToEntityAsync(TCreateInput createInput)
	{
		var entity = ObjectMapper.Map<TCreateInput, TEntity>(createInput);
		SetIdForGuids(entity);
		return Task.FromResult(entity);
	}

	/// <summary>
	/// 更新对象映射为实体
	/// </summary>
	/// <param name="updateInput">更新数据信息</param>
	/// <param name="entity">原更新数据实体</param>
	protected virtual Task MapToEntityAsync(TUpdateInput updateInput, TEntity entity)
	{
		ObjectMapper.Map(updateInput, entity);
		return Task.CompletedTask;
	}
	#endregion

	#region 自身方法
	private void SetIdForGuids(TEntity entity)
	{
		if (entity is IEntity<Guid> entityWithGuidId && entityWithGuidId.Id == Guid.Empty)
			EntityHelper.TrySetId(entityWithGuidId, () => GuidGenerator.Create(), true);
	}

	private void TryToSetTenantId(TEntity entity)
	{
		if (entity is IMultiTenant && HasTenantIdProperty(entity))
		{
			var tenantId = CurrentTenant.Id;
			if (!tenantId.HasValue)
				return;
			var propertyInfo = entity.GetType().GetProperty(nameof(IMultiTenant.TenantId));
			if (propertyInfo == null || propertyInfo.GetSetMethod(true) == null)
				return;
			propertyInfo.SetValue(entity, tenantId);
		}
	}

	private bool HasTenantIdProperty(TEntity entity)
	{
		return entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)) != null;
	}
	#endregion
}